<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module" src="../../polymer-legacy.js"></script>
</head>
<body>
  <script type="module">
window.configureBehavior = {

  changeHandlerCount: 0,
  objectChangeHandlerCount: 0,

  contentChanged: function() {
    this.changeHandlerCount++;
    this.stomp = 10;
  },
  objectChanged: function() {
    this.objectChangeHandlerCount++;
  }
};
</script>

  <dom-module id="x-configure-value">
    <template>
      <span id="content">{{content}}</span>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({

  is: 'x-configure-value',

  behaviors: [window.configureBehavior],

  properties: {
    content: {
      type: String,
      notify: true,
      observer: 'contentChanged',
      value: 'default'
    },
    object: {
      type: Object,
      notify: true,
      value: function() { return {foo: 'obj-default'}; },
      observer: 'objectChanged'
    },
    readOnly: {
      readOnly: true,
      value: 'default'
    },
    stomp: {
      value: 5
    }
  }

});
</script>
  </dom-module>

  <dom-module id="x-configure-gchild">
    <template>
      <span id="content">{{content}}</span>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({

  is: 'x-configure-gchild',

  behaviors: [window.configureBehavior],

  properties: {
    content: {
      type: String,
      notify: true,
      observer: 'contentChanged',
      value: 'gchild'
    },
    object: {
      type: Object,
      notify: true,
      value: function() { return {foo: 'obj-default'}; },
      observer: 'objectChanged'
    },
    readOnly: {
      readOnly: true,
      value: 'default'
    },
    stomp: {
      value: 5
    }
  }

});
</script>
  </dom-module>

  <dom-module id="x-configure-child">
    <template>
      <x-configure-gchild id="gchild" content="{{content}}" object="{{object}}"></x-configure-gchild>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({

  is: 'x-configure-child',

  behaviors: [window.configureBehavior],

  properties: {
    content: {
      type: String,
      notify: true,
      observer: 'contentChanged',
      value: 'child'
    },
    negatedContent: {
      type: Boolean,
      observer: 'negatedContentChanged',
      value: true
    },
    compoundInput: {
      type: String,
      observer: 'compoundInputChanged',
      value: 'default'
    },
    object: {
      type: Object,
      notify: true,
      value: function() { return {foo: 'obj-default'}; },
      observer: 'objectChanged'
    },
    readOnly: {
      readOnly: true,
      value: 'default'
    },
    stomp: {
      value: 5
    },
    attrDash: {
      observer: 'attrDashChanged',
      value: 'default'
    },
    attrNumber: {
      type: Number,
      observer: 'attrNumberChanged',
      value: 0
    },
    attrBoolean: {
      type: Boolean,
      observer: 'attrBooleanChanged',
      value: false
    }
  },

  created: function() {
    this.attrDashChanged = sinon.spy();
    this.attrNumberChanged = sinon.spy();
    this.attrBooleanChanged = sinon.spy();
    this.negatedContentChanged = sinon.spy();
    this.compoundInputChanged = sinon.spy();
  }

});
</script>
  </dom-module>

  <dom-module id="x-configure-simple-child">
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({

  is: 'x-configure-simple-child',

  properties: {
    noeffect: String
  },

  ready: function() {
    this.hasPropertyAtReadyTime = (this.noeffect !== undefined);
  }

});
</script>
  </dom-module>

  <dom-module id="x-configure-host">
    <template>
      <x-configure-child id="child"
          content="{{content}}"
          negated-content="[[!content]]"
          compound-input="a [[simple]] [[content]]"
          object="{{object.goo}}"
          attr$="{{attrValue}}"
          attr-dash$="{{attrValue}}"
          attr-number$="{{attrNumber}}"
          attr-boolean$="{{attrBoolean}}"
      ></x-configure-child>
      <x-configure-simple-child id="simple" noeffect="{{simple}}"></x-configure-simple-child>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({

  is: 'x-configure-host',

  behaviors: [window.configureBehavior],

  properties: {
    content: {
      type: String,
      notify: true,
      observer: 'contentChanged',
      value: 'host'
    },
    object: {
      type: Object,
      notify: true,
      value: function() { return {goo: {foo: 'obj-host'}}; },
      observer: 'objectChanged'
    },
    readOnly: {
      readOnly: true,
      value: 'default'
    },
    stomp: {
      value: 5
    },
    attrValue: {
      value: 'attrValue'
    },
    attrNumber: {
      value: 42
    },
    attrBoolean: {
      value: false
    },
    simple: {
      value: 'simple'
    }
  }
});
</script>
  </dom-module>

  <script type="module">
window.XConfigLazy = {
  is: 'x-config-lazy',
  properties: {
    noEffectProp: Number,
    defaultUsesNoEffectProp: {
      type: Number
    },
    boundNoEffectProp: {
      type: Number,
      value: 5
    },
    prop: {
      value: 'lazy',
      observer: 'propChanged'
    },
    readOnlyProp: {
      readOnly: true,
      value: 'readOnly'
    },
    hadAttrProp: {
      value: 'hadAttrProp',
      observer: 'hadAttrPropChanged'
    }
  },
  created: function() {
    this.noEffectProp = 1;
    this.defaultUsesNoEffectProp = this.noEffectProp * 2;
    this.propChanged = sinon.spy();
    this.hadAttrPropChanged = sinon.spy();
  }
};
</script>

  <dom-module id="x-config-lazy-nodefaults">
    <template>
      <div>x-config-lazy-nodefaults</div>
    </template>
    <script type="module">
window.XConfigLazyNoDefaults = {
  is: 'x-config-lazy-nodefaults',
  properties: {
    prop: {
      observer: 'propChanged'
    }
  },
  created: function() {
    this.propChanged = sinon.spy();
  }
};
</script>
  </dom-module>

  <dom-module id="x-config-lazy-host">
    <template>
      <x-config-lazy id="lazy" prop="{{foo}}" read-only-prop="{{foo}}" had-attr-prop="attrValue" bound-no-effect-prop="{{foo}}"></x-config-lazy>
      <x-config-lazy-nodefaults prop="[[foo]]"></x-config-lazy-nodefaults>
    </template>
    <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-config-lazy-host',
  properties: {
    foo: {
      value: 'foo',
      observer: 'fooChanged'
    }
  },
  fooChanged: function(foo) {
    this.$.lazy.hadAttrProp = foo;
  }
});
</script>
  </dom-module>

  <x-configure-value></x-configure-value>

  <x-configure-value content="attr" object='{"foo": "obj-attr"}'></x-configure-value>

  <x-configure-host></x-configure-host>

  <x-configure-host content="attr"></x-configure-host>

<script type="module">
import { Polymer } from '../../polymer-legacy.js';

function testValueAndChangeHandler(e, value) {
  assert.equal(e.content, value, 'Property does not equal configured value');
  assert.equal(e.changeHandlerCount, 1, 'property `change` Change handler not run when default value set');
  assert.equal(e.objectChangeHandlerCount, 1, 'property `object` Change handler not run when default value set');
}

function testConfigure(e, value, objectValue) {
  testValueAndChangeHandler(e, value);
  assert.equal(e.object.foo, objectValue);
  assert.equal(e.$.content.textContent, value, 'Bound value not propagated to dom');
}

function testConfigureHost(e, value) {
  testValueAndChangeHandler(e, value);
  e = e.$.child;
  testValueAndChangeHandler(e, value);
  e = e.$.gchild;
  testValueAndChangeHandler(e, value);
  assert.equal(e.$.content.textContent, value, 'Bound value not propagated to dom');
}

suite('configure', function() {

  test('value set in properties initializes correctly', function() {
    var e = document.querySelector('x-configure-value');
    testConfigure(e, 'default', 'obj-default');
  });

  test('attribute overrides value set in properties', function() {
    var e = document.querySelector('x-configure-value[content]');
    testConfigure(e, 'attr', 'obj-attr');
  });

  test('configured values initialize and propagates', function() {
    var e = document.querySelector('x-configure-host');
    testConfigureHost(e, 'host');
  });

  test('negated value configured correctly', function() {
    var e = document.querySelector('x-configure-host');
    assert.equal(e.$.child.negatedContent, false);
    assert.isTrue(e.$.child.negatedContentChanged.calledOnce, 'negated content not changed exactly once');
  });

  test('compound effect resulting value set once', function() {
    var e = document.querySelector('x-configure-host');

    assert.equal(e.$.child.compoundInput, 'a simple host');
    assert.isTrue(e.$.child.compoundInputChanged.calledOnce, 'compound content not changed exactly once');
  });

  test('attribute overrides configured values and propagates', function() {
    var e = document.querySelector('x-configure-host[content]');
    testConfigureHost(e, 'attr');
  });

  test('property changed in change handler of another not stomped by default', function() {
    var e = document.querySelector('x-configure-value');
    assert.equal(e.stomp, 10);
  });

  test('read-only property initialized to default value', function() {
    var e = document.querySelector('x-configure-value');
    assert.equal(e.readOnly, 'default');
  });

  test('attribute bindings to properties without effects not configured', function() {
    var e = document.querySelector('x-configure-host');
    assert.equal(e.$.child.getAttribute('attr'), 'attrValue');
    assert.equal(e.$.child.attr, undefined);
  });

  test('attribute bindings to properties with effects configured', function() {
    var e = document.createElement('x-configure-host');
    document.body.appendChild(e);

    assert.equal(e.$.child.getAttribute('attr-dash'), 'attrValue');
    assert.notProperty(e.$.child, 'attr-dash');
    assert.equal(e.$.child.attrDash, 'attrValue');
    assert.isTrue(e.$.child.attrDashChanged.calledOnce);
    assert.equal(e.$.child.attrDashChanged.getCall(0).args[0], 'attrValue');

    assert.equal(e.$.child.getAttribute('attr-number'), '42');
    assert.notProperty(e.$.child, 'attr-number');
    assert.strictEqual(e.$.child.attrNumber, 42);
    assert.isTrue(e.$.child.attrNumberChanged.calledOnce);
    assert.strictEqual(e.$.child.attrNumberChanged.getCall(0).args[0], 42);

    assert.equal(e.$.child.hasAttribute('attr-boolean'), false);
    assert.notProperty(e.$.child, 'attr-boolean');
    // Attribute bindings are no longer specially
    // configured to properties as they were with 1.0. This should
    // only affect bindings that set false (removing a non-existing attribute)
    // to override a default of true (which is an odd default for a boolean attr).
    assert.strictEqual(e.$.child.attrBoolean, false);
    assert.isTrue(e.$.child.attrBooleanChanged.calledOnce);
    assert.strictEqual(e.$.child.attrBooleanChanged.getCall(0).args[0], false);
  });

  test('bindings to properties without effects configured', function() {
    var e = document.createElement('x-configure-host');
    document.body.appendChild(e);
    assert.isTrue(e.$.simple.hasPropertyAtReadyTime, 'property value not configured and therefore not set at ready time');
  });

  test('pre-register property assignment does not break getters and setters', function() {
    var x = document.createElement('x-late-register');
    document.body.appendChild(x);
    // set property
    x.shouldChange = '1';
    // now register element
    Polymer({
      is: 'x-late-register',
      properties: {
        shouldChange : {
          observer: 'shouldChangeCallback',
          type: String
        }
      },
      shouldChangeCallback: function() {
        this.textContent = this.shouldChange;
      }
    });
    assert.equal(x.shouldChange, '1');
    assert.equal(x.shouldChange, x.textContent);
    x.shouldChange = '2';
    assert.equal(x.shouldChange, '2');
    assert.equal(x.shouldChange, x.textContent);
    document.body.removeChild(x);
  });

  // Created is no longer called before
  // default properties are set. This means values set in created cannot
  // be used to calculate defaults. However, defaults can now be set for
  // any property (including those with effects) in created.
  test('setting properties in created works with configuration', function() {
    var x = document.createElement('x-late-register2');
    document.body.appendChild(x);
    // now register element
    Polymer({
      is: 'x-late-register2',
      properties: {
        a: {
          type: Number,
          value: 0
        },
        b: {
          type: Number
        }
      },
      created: function() {
        this.a = 1;
        this.b = this.a * 2;
      }
    });
    assert.equal(x.b, 2);
    document.body.removeChild(x);
  });

  test('lazy upgrade binding use cases', function() {
    var el = document.createElement('x-config-lazy-host');
    document.body.appendChild(el);
    Polymer(window.XConfigLazy);
    // The purpose of this test is to test lazy upgrading an element that
    // has a property pre-bound to it (that has an accessor) but that has
    // no defaults for it (stresses a prior bug in ready() code that called
    // _attachDom twice, which throws in Safari's native SD
    Polymer(window.XConfigLazyNoDefaults);
    assert.equal(el.$.lazy.noEffectProp, 1);
    assert.equal(el.$.lazy.defaultUsesNoEffectProp, 2);
    assert.equal(el.$.lazy.boundNoEffectProp, 'foo');
    assert.equal(el.$.lazy.prop, 'foo');
    assert.isTrue(el.$.lazy.propChanged.calledOnce);
    assert.equal(el.$.lazy.readOnlyProp, 'readOnly');
    assert.equal(el.$.lazy.hadAttrProp, 'foo');
    assert.isTrue(el.$.lazy.hadAttrPropChanged.calledOnce);
    document.body.removeChild(el);
  });

});
</script>
</body>
</html>
